package tron.errorhandler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@ControllerAdvice
@Slf4j
public class TronExceptionHandler {


    /**
     * 处理没有被明确指定处理类的异常信息
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String,Object>> errorHandler(Exception e) {
        //后台处理
        StackTraceElement[] stackTrace = e.getStackTrace();
        StackTraceElement traceElement = stackTrace[0];
        log.error("\"message\":\"{}\"",traceElement.getClassName()+":"+traceElement.getLineNumber()+"--->"+e.getMessage());
        //前端友好提示
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", e.getMessage());
        map.put("code", 3000);
        //修改返回头状态码
        return new ResponseEntity<>(map, HttpStatus.I_AM_A_TEAPOT);
    }


    /**
     * 举例 GET 方法用POST请求   错误的请求类型
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Map<String, Object> HttpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", "错误的请求类型");
        map.put("code", 3001);
        return map;
    }

    /**
     * 请求参数缺失异常
     * 举例： @RequestParam String a 但是路径中没有a值
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Map<String, Object> missingServletRequestParameterExceptionHandler(HttpServletRequest request, MissingServletRequestParameterException e) {
        //获取请求状态
        HttpStatus status = getStatus(request);
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", e.getMessage());
        map.put("code", 3002);
        map.put("status", status);
        return map;
    }

    /**
     * 断言异常处理
     * 举例 Assert.isTrue(a.startsWith("tron"),"断言异常");
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(IllegalArgumentException.class)
    public Map<String, Object> illegalStateExceptionHandler(IllegalArgumentException e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", e.getMessage());
        map.put("code", 3003);
        return map;
    }


    /**
     * 举例 数值转换异常    "a" -> Date
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(ConversionFailedException.class)
    public Map<String, Object> ConversionFailedExceptionHandler(ConversionFailedException e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", e.getMessage());
        map.put("code", 3004);
        return map;
    }


    /**
     * 请求体格式不支持
     * 举例  Content type 'multipart/form-data  ->  json
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Map<String, Object> HttpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("message", e.getMessage());
        map.put("code", 3006);
        return map;
    }


    /**
     * 方法参数校验异常处理 post json 校验
     * 举例  @RequestBody @Validated(value = UpdateGroup.class) ValidateData validateData
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        HashMap<String, Object> map = new HashMap<>();
        BindingResult bindingResult = e.getBindingResult();
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        getErrors(map, allErrors);
        return map;
    }

    /**
     * 参数绑定异常处理 get请求
     * 举例 @Validated(value = TronGroup.class) ValidateData validateData
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    public Map<String, Object> bindExceptionHandler(BindException e) {
        HashMap<String, Object> map = new HashMap<>();
        List<ObjectError> allErrors = e.getAllErrors();
        getErrors(map, allErrors);
        return map;
    }


    private void getErrors(HashMap<String, Object> map, List<ObjectError> allErrors) {
        StringBuffer stringBuffer = new StringBuffer();
        allErrors.forEach(x -> stringBuffer.append(x.getObjectName()).append("--->").append(x.getDefaultMessage()).append(";"));
        map.put("message", stringBuffer.toString());
        map.put("code", 3007);   //参数校验异常
    }


    //获取请求状态码
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}
